﻿#ifndef _MEMORYMGR_HPP_
#define _MEMORYMGR_HPP_

#include <stdlib.h>
#include <assert.h>
#include <mutex>
#include <string.h>

// #define MAX_MEMORY_SIZE 64
#define MAX_MEMORY_SIZE 256
// #define _DEBUG

// 调式信息
#ifdef ____DEBUG
#include <stdio.h>
#ifndef xPrintf
#define xPrintf(...) printf(__VA_ARGS__)
#endif // #ifndef xPrintf(...)
#else
#ifndef xPrintf
#define xPrintf(...)
#endif // #ifndef xPrintf(...)
#endif // #ifdef _DEBUG

class MemoryAlloc;

// 内存块 最小单元
class MemoryBlock
{
public:
	// 所属内存块(池)
	MemoryAlloc* pAlloc;
	// 下一块位置
	MemoryBlock* pNext;
	// 内存块编号
	int nID;
	// 引用次数
	int nRef;
	// 是否在内存池中
	bool bPool;
private:
	// 预留
	char c1;
	char c2;
	char c3;
};

// 内存池
class MemoryAlloc
{
public:
	MemoryAlloc() : _pBuf(nullptr), _pHeader(nullptr), _nSize(0), _nBlockSize(0)
	{

	}

	~MemoryAlloc()
	{
		if (_pBuf)
		{
			free(_pBuf);
		}
	}

	void* allocMemory(size_t nSize)
	{
		std::lock_guard<std::mutex> lg(_mutex);
		if (!_pBuf)
		{
			InitMemory();
		}
		MemoryBlock* pReturn = nullptr;
		if (nullptr == _pHeader)
		{
			pReturn = (MemoryBlock*)malloc(nSize + sizeof(MemoryBlock));
			pReturn->bPool = false;
			pReturn->nID = -1;
			pReturn->nRef = 1;
			pReturn->pAlloc = nullptr;
			pReturn->pNext = nullptr;
			// 超出内存池时输出
			printf("allocMem: %lx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize);
		}
		else
		{
			pReturn = _pHeader;
			_pHeader = _pHeader->pNext;
			assert(0 == pReturn->nRef);
			pReturn->nRef = 1;
		}
		xPrintf("allocMem: %lx, id=%d, size=%d", pReturn, pReturn->nID, nSize);
		// 内存前面是这块内存的信息，给用户使用把信息给去掉
		return ((char*)pReturn + sizeof(MemoryBlock));
	}

	void freeMemory(void* pMem)
	{
		// 内存前面是这块内存的信息，要得到信息就要偏移到内存的前面
		MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
		assert(1 == pBlock->nRef);
		if (pBlock->bPool)
		{// 在内存池中的直接放回内存池就行
			std::lock_guard<std::mutex> lg(_mutex);
			if (--pBlock->nRef != 0)
			{
				return;
			}
			pBlock->pNext = _pHeader;
			_pHeader = pBlock;
		}
		else
		{// 在内存池外直接就是free
			if (--pBlock->nRef != 0)
			{
				return;
			}
			free(pBlock);
		}
	}

	// 初始化
	void InitMemory()
	{
		xPrintf("initMemory:_nSize=%d,_nBlockSize=%d", _nSize, _nBlockSize);
		assert(nullptr == _pBuf);
		if (_pBuf)
			return;
		size_t offsetSize = _nSize + sizeof(MemoryBlock);
		// 计算内存池大小(内存单元大小*内存单元数量)
		size_t bufSzie = offsetSize * _nBlockSize;
		// 向系统申请池内存
		_pBuf = (char*)malloc(bufSzie);
		// 初始化内存池
		_pHeader = (MemoryBlock*)_pBuf;
		_pHeader->bPool = true;
		_pHeader->nID = 0;
		_pHeader->nRef = 0;
		_pHeader->pAlloc = this;
		_pHeader->pNext = nullptr;

		MemoryBlock* pCur = _pHeader;
		MemoryBlock* pNext = nullptr;
		// 从头部开始，让每一块内存单元都指向下一块内存单元
		for (size_t n = 1; n < _nBlockSize; ++n)
		{
			pNext = (MemoryBlock*)(_pBuf + n * offsetSize);
			pNext->bPool = true;
			pNext->nID = n;
			pNext->nRef = 0;
			pNext->pAlloc = this;
			pNext->pNext = nullptr;

			pCur->pNext = pNext;
			pCur = pNext;
		}
	}
protected:
	// 内存池地址
	char* _pBuf;
	// 头部头部单元
	MemoryBlock* _pHeader;
	// 内存单元的大小
	size_t _nSize;
	// 内存单元的数量
	size_t _nBlockSize;
	std::mutex _mutex;
};

// 这个确定类型的模板类用于确定内存池的内存单元大小和内存单元数量
template<size_t nSize, size_t nBlockSize>
class MemoryAlloctor : public MemoryAlloc
{
public:
	MemoryAlloctor()
	{
		// 如果nSize不是内存对齐的数 4(32位)或8(64位)的倍数
		// 指针大小在32位操作系统中是4字节，在64位操作系统中是8字节
		const size_t n = sizeof(void*);
		MemoryAlloc::_nSize = (nSize / n) * n + (nSize % n ? n : 0);
		MemoryAlloc::_nBlockSize = nBlockSize;
	}
};

// 内存管理
class MemoryMgr
{
public:
	// 单例
	static MemoryMgr& Instance()
	{
		static MemoryMgr mgr;
		return mgr;
	}

	void* allocMem(size_t nSize)
	{
		if (nSize <= MAX_MEMORY_SIZE)
		{
			return _szAlloc[nSize]->allocMemory(nSize);
		}
		else
		{
			MemoryBlock* pReturn = (MemoryBlock*)malloc(nSize + sizeof(MemoryBlock));
			pReturn->bPool = false;
			pReturn->nID = -1;
			pReturn->nRef = 1;
			pReturn->pAlloc = nullptr;
			pReturn->pNext = nullptr;
			xPrintf("allocMem: %lx, id=%d, size=%d", pReturn, pReturn->nID, nSize);
			return ((char*)pReturn + sizeof(MemoryBlock));
		}
	}

	void freeMem(void* pMem)
	{
		// 内存前面是这块内存的信息，要得到信息就要偏移到内存的前面
		MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
		xPrintf("freeMem: %lx, id=%d", pBlock, pBlock->nID);
		if (pBlock->bPool)
		{// 在内存池中的直接放回内存池就行 在freeMemory会自己转换，所以传递pMem
			pBlock->pAlloc->freeMemory(pMem);
		}
		else
		{// 在内存池外直接就是free
			if (--pBlock->nRef == 0)
				free(pBlock);
		}
	}

	// 增加1内存块的引用计数，暂时用不上
	void AddRef(void* pMem)
	{
		MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock));
		++pBlock->nRef;
	}
private:
	// 初始化内存池映射数组
	void Init_szAlloc(int nBegin, int nEnd, MemoryAlloc* pMem)
	{
		for (int i = nBegin; i <= nEnd; ++i)
		{
			_szAlloc[i] = pMem;
		}
	}
	MemoryMgr()
	{
		Init_szAlloc(0, 64, &_mem64);
		Init_szAlloc(65, 128, &_mem128);
		Init_szAlloc(129, 256, &_mem256);
		// Init_szAlloc(257, 512, &_mem512);
		// Init_szAlloc(513, 1024, &_mem1024);
	}
	~MemoryMgr()
	{
		
	}
	// 内存池的每个内存单元是64字节大小，共有100000块内存单元
	MemoryAlloctor<64, 100000> _mem64;
	// 内存池的每个内存单元是128字节大小，共有100000块内存单元
	MemoryAlloctor<128, 100000> _mem128;
	// // 内存池的每个内存单元是256字节大小，共有100000块内存单元
	MemoryAlloctor<256, 100000> _mem256;
	// // 内存池的每个内存单元是256字节大小，共有100000块内存单元
	// MemoryAlloctor<512, 100000> _mem512;
	// MemoryAlloctor<1024, 100000> _mem1024;
	MemoryAlloc* _szAlloc[MAX_MEMORY_SIZE + 1];
};

#endif // #ifndef _MEMORYMGR_HPP_